#include <netdb.h> 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
# include <iostream>
#include <string>
#include <vector>
#include <signal.h>
#include <sstream>
#include <map>
#include "client.h"

using namespace std;
pthread_mutex_t mutexread;
bool Client::isElectionInvoked;
vector <bool> Client :: checkServerAlive;
vector <bool> Client :: checkNextServerAlive;
int Client :: currentInstanceNum;
int Client :: currentTryNum;
int Client :: currentNewSeqTryNum;
bool Client :: convertClientToSequencer;
bool Client :: isSequencerFound;


long long getlongFromIP(string str){

 string ip;
 string delimiters = ".";
 size_t current;
 size_t next = -1;
 long long ld=0;
 do
 {
    current = next + 1;
    next = str.find_first_of( delimiters, current ); 
 //   cout<<"substr ="<< str.substr( current, next - current )<<endl;
    ip+=str.substr( current, next - current );
 }
 while (next != string::npos);
  
 //cout<<"final ip="<<ip<<endl;
 istringstream ss(ip);
 ss>>ld;
 return ld;

}

string getMaxIPFromIndices(vector<int> ipIndices){

  string str=DistChat::currIPtable[ipIndices[0]];
  //cout<<"In max function"<<endl;
  for(int i=1;i<ipIndices.size();i++){
     long long l1= getlongFromIP(DistChat::currIPtable[ipIndices[i]]);
     long long l2 = getlongFromIP(str);	
     if(l1>l2)
	str=DistChat::currIPtable[ipIndices[i]];
  }
  return str;

}
void Client :: electionHandler(int sig)
{ 
  if(isSequencerFound == false) 
  {
   // cout << "Within election handler!!!" <<endl;
    //First check who has the highest IP
    vector<int> largerIndex; 
    vector<long double> longIPs;
    //Store indices with larger IP. currently comparing strings, need to compare the ints
    cout <<"Current Client : "<< DistChat::currentClientIP <<" : "<<currIPtable.size() <<endl;
    for(int z = 0; z < currIPtable.size(); z++){
      if(currIPtable[z].compare(DistChat::currentClientIP) != 0){
	long long l1 = getlongFromIP(currIPtable[z]);
	cout << l1 <<endl;
	long long l2 = getlongFromIP(DistChat::currentClientIP);
	cout << l2 <<endl;		
        if(l1>l2)
          largerIndex.push_back(z);
      }
    }
    
   // for(int i=0;i<largerIndex.size();i++)
   //   cout<<largerIndex[i]<<endl;
  //      cout <<"Comes here 1 "<<endl;	
    
    string maxIP;	
    if(largerIndex.size() == 0)
      maxIP = DistChat::currentClientIP;
    else
      maxIP=getMaxIPFromIndices(largerIndex);
    
    cout <<"max IP  : " <<maxIP <<endl;
    /*bool cameInLoop = false;
      for(int w=0; w< checkNextServerAlive.size(); w++)
      {
      if(checkNextServerAlive[w] == true)
      cameInLoop = true;
      //Remve the maxIP from the IP table!                 
      }
      
      cout <<"Comes here 2 "<<endl;	
      if(cameInLoop  == false)
      {
      cout << "comes here 5 "<<endl;
      for(int z =0; z < currIPtable.size(); z++)
      if(currIPtable[z].compare(maxIP) == 0)
      {
      currIPtable.erase(currIPtable.begin() + z);
      for(int p = 0; p < largerIndex.size(); p++)
      {
      if(largerIndex[p] == z)
      {
      largerIndex.erase(largerIndex.begin() + p);
      
      if(largerIndex.size() != 0)
      {
      //Update the maxIP;
      //maxIP = "SOMETHING ELSEEEEEEEEEEEEEEE..HAVE TO FILL IN......................"; //--------------------------->
      maxIP=getMaxIPFromIndices(largerIndex);
      currentNewSeqTryNum = 0;
      }
      }
      
      }
      }
      }
    */
    
   // cout <<"Comes here 3 "<<endl;		
    if(largerIndex.size() == 0 && isSequencerFound == false)
      {
	//cout <<"Comes here 4 "<<endl;	
	//I am the leader. So, I become the sequencer in this case.
	alarm(0);
	convertClientToSequencer = true;
	for(int w = 0; w < currIPtable.size(); w++)
	  {
	  if(currIPtable[w].compare(DistChat::currentClientIP) != 0)
	    {
	      
	      char* argv[3];
	      argv[0]="abcd\0";
	      string str = currIPtable[w]; // all other clients alive
	      char * writable = new char[str.size() + 1];
	      std::copy(str.begin(), str.end(), writable);
	      writable[str.size()] = '\0';
	      argv[1]=writable;
	      char num[5];
	      sprintf(num, "%d", chatport);
	      argv[2]=num;
	      
	      Message m;
	      m.messagetype = MSG_UPDATECURRENTSEQUENCER;
	      m.message =  DistChat::currentClientIP;
	      
	      stringstream temp(stringstream::in | stringstream::out);
	      temp<<m;
	      send(argv,temp.str().c_str());
	      delete[] writable;
	    }
	  }
	
	char* argv[3];
	argv[0]="abcd\0";
	string str = currentClientIP; // MESSAGE TO SELF
	char * writable = new char[str.size() + 1];
	std::copy(str.begin(), str.end(), writable);
	writable[str.size()] = '\0';
	argv[1]=writable;
	char num[5];
	sprintf(num, "%d", chatport);
	argv[2]=num;
	
	Message m;
	m.messagetype = MSG_SAFETOKILLSELFNOW;
	m.message =  DistChat::currentClientIP;
	
	stringstream temp(stringstream::in | stringstream::out);
	temp<<m;
	send(argv,temp.str().c_str());
	delete[] writable;
      }
    else
      {
	
	//PINGING ALGORITHM
//	cout <<"Comes here 6"<<endl;	
	char* argv[3];
	argv[0]="abcd\0";
	string str= maxIP; // all IPS that are higher than this one!!
	char * writable = new char[str.size() + 1];
	std::copy(str.begin(), str.end(), writable);
	writable[str.size()] = '\0';
	argv[1]=writable;
	char num[5];
	sprintf(num, "%d", chatport);
	argv[2]=num;
	
	Message m;
	checkNextServerAlive[currentNewSeqTryNum%TRIES] = false;
	m.messagetype = MSG_CHECKNEXTSERVERALIVE;
	m.message =  DistChat::currentClientIP;
	m.instancenumber = currentNewSeqTryNum++;
	stringstream temp(stringstream::in | stringstream::out);
	temp<<m;
	send(argv,temp.str().c_str());
	delete[] writable;
	
      }
  }
}


void Client :: handler(int sig)
{
  //This is the handler that is called every second
  
  alarm(1);
  // cout<<Client::isElectionInvoked<<" is what???? "<<endl;
  if(isElectionInvoked==false)
    {
      int index = 0;
      bool flag=false;
      // cout << "Sending to : " <<hostIP <<endl;
      //cout <<"Server - are you alive?? : "<<currentTryNum <<endl;	
      for(index =0; index < checkServerAlive.size(); index++)
	{
	  
	  // cout <<checkServerAlive[index] << " ";
	  if(checkServerAlive[index] == true)
	    flag=true;
	}
      // cout <<endl;
      if(flag==false)
	{
	  cout <<"LOOKS LIKE SERVER IS DOWN!!!!!!!!!!!!!!!!!!!!!! " <<endl;
	 // cout <<"STARTING ELECTION ALGORITHM" <<endl;
	  isElectionInvoked=true;
	  currentNewSeqTryNum = 0;
	  isSequencerFound = false;
	  electionHandler(1);
	}
      char* argv[3];
      argv[0]="abcd\0";
      string str=hostIP;
      char * writable = new char[str.size() + 1];
      std::copy(str.begin(), str.end(), writable);
      writable[str.size()] = '\0';
      argv[1]=writable;
      char num[5];
      sprintf(num, "%d", chatport);
      argv[2]=num;
      
      checkServerAlive[currentTryNum%TRIES] = false;
      Message m;
      m.messagetype = MSG_CLIENTCHECKSALIVE;
      m.message =  DistChat::currentClientIP;
      m.instancenumber = currentTryNum++;
      stringstream temp(stringstream::in | stringstream::out);
      temp<<m;
      send(argv,temp.str().c_str());
      delete[] writable;
      
    }
  else
    {
     // cout <<"Election handler method" <<endl;
      
      electionHandler(1);
    }
}


void * Client :: callForListening(void * data)
{
  //cout <<"CALLING THREAD LISTEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " <<endl;
  listeningThData * dataReceived = (listeningThData *) data;
  int portNum = dataReceived->portNum;
  listen(dataReceived->portNum);
}

void * Client ::callForCheckingAlive(void * data)
{
  //cout <<"This thread is setting the alarm!!!" <<endl;
  signal( SIGALRM, &Client::handler);
  alarm(1);
  
 //Set instanceNumber! 

}

void * Client :: callForUserInput(void * data)
{
  //cout <<"CALllllllllllign user inputtttttttttt thread !!!!!!!!!!!!!!!!!" <<endl;
  int count = 0;
  while(1)  
    {
      char* argv[3];
      argv[0]="abcd\0";
      string str=hostIP;
      char * writable = new char[str.size() + 1];
      std::copy(str.begin(), str.end(), writable);
      writable[str.size()] = '\0';
      argv[1]=writable;
      char num[5];
      sprintf(num, "%d", chatport);
      argv[2]=num;
      char * ipbuf=getIPAddress();
      char send_data[MSG_SIZE];
	if( convertClientToSequencer == true)
	{
	//cout <<"Killing userinput thread for client "<<endl;
	void * retval = NULL;
	  pthread_exit(retval);
	}
      pthread_mutex_lock(&mutexread);
      cout<<"Type Something (q or Q to quit):"<<endl;
    //  if( convertClientToSequencer == true)
	//cout<<"Convert this client to sequencer" <<endl;
      //else
	//cout <<"Still a client!!" <<endl;
      cin.getline(send_data, MSG_SIZE);
      Message m;
      m.seqno = DistChat :: currentClientSequenceNumber++;
      m.username=userName;
      m.message=send_data;
      m.messagetype = MSG_DATAMSG;
      DistChat::sentMessages.push_back(m);
    //  cout << "SENT MESSAGES SIZE : " <<endl;
    //  for (int zzzz= 0; zzzz < sentMessages.size(); zzzz++)
	//cout <<zzzz << " : " << sentMessages[zzzz]<<endl;
    
      if ((strcmp(send_data , "q") == 0) || strcmp(send_data , "Q") == 0)
	{
	   Message removeClient;
	   removeClient.messagetype = MSG_REMOVEFROMIPTABLE;
	   removeClient.message = DistChat::currentClientIP;
	   char* argv[3];
	   argv[0]="abcd\0";
	   string str = hostIP;
	   char * writable = new char[str.size() + 1];
	   std::copy(str.begin(), str.end(), writable);
	   writable[str.size()] = '\0';
	   argv[1]=writable;
	   char num[5];
	   sprintf(num, "%d", chatport);
	   argv[2]=num;
	   
	   stringstream temp(stringstream::in | stringstream::out);
	   temp<<removeClient;
	   send(argv,temp.str().c_str());
	   delete[] writable;







	  pthread_mutex_unlock(&mutexread);
	//char* argv[3];
	  argv[0]="abcd\0";
	  str = currentClientIP; // MESSAGE TO SELF
	  writable = new char[str.size() + 1];
	  std::copy(str.begin(), str.end(), writable);
	  writable[str.size()] = '\0';
	  argv[1]=writable;
	  //char num[5];
	  sprintf(num, "%d", chatport);
	  argv[2]=num;
	
	Message m;
	m.messagetype = MSG_NOWSAFETOKILLSELFNOW;
	m.message =  DistChat::currentClientIP;
	
	stringstream temp2(stringstream::in | stringstream::out);
	temp2<<m;
	send(argv,temp2.str().c_str());
	delete[] writable;
	//TEST WITH KILLING THE THREAD............
	//  cout <<"Killing this thread "<<endl;
          void * retval = NULL;
	  pthread_exit(retval);

	


	  break;
	}
      else
	{
	//  cout <<ipbuf <<"sent a message to sequencer. "<<endl;
	  stringstream temp(stringstream::in | stringstream::out);
	  temp<<m;
	  send(argv,temp.str().c_str());
	}
      pthread_mutex_unlock(&mutexread);
      delete[] writable;
      
    }
}

Client::~Client(){
  //cout <<" CLIENT Destructor called "<<endl;
  close(DistChat::sock);

}

Client :: Client(char * argv[])
{
  DistChat::currentClientSequenceNumber = 0;                                                                                  
  DistChat::firstMessage = true;
  Client::isElectionInvoked=false;
  Client::convertClientToSequencer = false;
  Client::isSequencerFound = false;
  currentTryNum = 0;
  currentNewSeqTryNum = 0;
  currentInstanceNum = 0;
  checkServerAlive.resize(TRIES);
  checkNextServerAlive.resize(TRIES);
 
  for(int i = 0; i < checkServerAlive.size() ; i++)
    checkServerAlive[i] = true;
  for(int i = 0; i < checkNextServerAlive.size() ; i++)
    checkNextServerAlive[i] = true;
  
  pthread_t threadClientListens;
  pthread_t threadUserInput;
  pthread_t threadForCheckingAlive;
  
  listeningThData dataToReceive;
  dataToReceive.portNum= atoi(argv[3]);
  chatport=atoi(argv[3]);;
  hostIP=argv[2];
  strcpy( userName,argv[1]);
 
  int tUi = pthread_create(&threadUserInput, NULL, &Client::callForUserInput, &dataToReceive);
  int tCl = pthread_create(&threadClientListens, NULL, &Client::callForListening, &dataToReceive);
  int tCAl = pthread_create(&threadForCheckingAlive, NULL, &Client::callForCheckingAlive, &dataToReceive);
  pthread_join(threadClientListens, NULL);
 
 
  if(tUi){
    printf("ERROR; return code from pthread_create() is %d\n", tCl);
    exit(-1);
  }
}
